#!/usr/bin/ruby

require 'rubygems'
require 'ruby-osc'
require 'gosu'
require 'gl'
require 'glu'

module Geometrics

  class Pattern
    attr_accessor :pos,:color,:hidden, :in_scene

    TYPES = { :red => [1,0,0], :green => [0,1,0], :blue => [0,0,1],
              :black => [0,0,0], :pink => [1,0,1], :yellow => [1,1,0]
    }

    MESSAGES = {
      :red => 3, :green => 2 , :blue => 4, :black => 5, :pink => 2, :yellow => 2
    }

    def initialize p,c
      @pos , @type, @hidden = p,c, false
      @color = TYPES[@type] or raise "Fuck"
      @rate = 1.0
      @in_scene = true

    end

    def x
        @pos[0]
    end

    def y
        @pos[1]
    end

    def hide
      @hidden = true
    end

    def touching? y
#      if !@in_scene
#        puts 'not touch'
#        false
#      else
        (y - @pos[1]).abs < 0.0001
      #end
    end

    def regenerate
      @hidden = false
      @pos[1] = 1
    end

    def message
      ["/rusic/drums", MESSAGES[@type], @rate]
    end

    def out_of_scene
      @in_scene = false
    end

  end

  OBJS = {}

  def load_obj file
    c = IO.read(file)
    obj = { 'v' => [], 'vn' => [], 'f' => [] }

    c.each_line do |l|
      l = l.split
      first = l.delete_at 0
      obj[first] << l if ['v','vn','f'].include? first
    end

    obj['v'] = obj['v'].each.map { |x| x.map(&:to_f) }
    obj['vn'] = obj['vn'].each.map { |x| x.map(&:to_f) }
    obj['f'] = obj['f'].each.map { |x| x.map! { |y| y.split("/").map { |z| z.to_i - 1} } }

    OBJS[file] = obj
    return obj
  end

  def draw_obj obj_name

    return unless obj = OBJS[obj_name]

    glBegin GL_TRIANGLES

      obj['f'].each do |f|
        f.each do |vi|
          glNormal3fv( obj['vn'][vi[2]] )
          glVertex3fv( obj['v'][vi[0]] )
        end

      end

    glEnd
  end
end


include OSC
include Gl
include Glu
include Gosu
include Geometrics

require 'pp'

CLIENT = Client.new 6666 , 'localhost'
SERVER = Server.new 9090

def send_message url, *params
    begin
      CLIENT.send Message.new(url,*params)
    rescue Exception => e
      pp   "Failed sending", url, *params
    end
end

class Game < Gosu::Window

    SCREEN_WIDTH, SCREEN_HEIGHT = 1366,400

    def initialize
        super(SCREEN_WIDTH, SCREEN_HEIGHT, false)
        self.caption = "HCI"

        @bpm = 120

        @notes = [   62,   62,   64,   62,   67,   66,
                     62,   62,   64,   62,   69,   67,
                     62,   62,   71,   69, 67, 66, 62,
                     72,   72,   71,   67,   69,   67
        ]

        @times = [  0.5,  0.5,    1,    1,    1,    2,
                    0.5,  0.5,    1,    1,    1,    2,
                    0.5,  0.5,    1,    1,  0.5,  0.5,   2,
                    0.5,  0.5,    1,    1,    1,    10,
        ]

        @note_index = -1

        @angle = 30
        @colors = [
            [0,0.5,0],
            [0.8,0,0],
            [0.5,0.5,0],
            [0,0,0.5],
            [0.5,1,0.5],
        ]
        @color = @colors[0]

        gl_init
        load_obj 'cube.obj'
        
        SERVER.add_pattern /.*/ do |*note|
            @color = @colors[note[1]%5] 
            puts 'hit note', note
        end
        
        @old_time = milliseconds
        @note_x = -9
    end

    def current_note
        @notes[@note_index]
    end
    
    def note_time
        @times[@note_index]*1000*60/@bpm
    end

    def advance_note
        @note_index += 1
        @note_index = 0 if @note_index == @notes.size
    end

    def gl_init
        glClearColor(@color[0],@color[1],@color[2],1)
        glClearDepth(0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glDepthFunc(GL_GEQUAL)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_BLEND)
        glEnable(GL_CULL_FACE)

        glMatrixMode(GL_PROJECTION)
        glLoadIdentity

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity

        glEnable(GL_LIGHTING)
        glEnable(GL_COLOR_MATERIAL)
        glShadeModel(GL_SMOOTH)
        glEnable(GL_LIGHT0)
        glEnable(GL_NORMALIZE)
    end

    def lights

      color = [1, 1, 1, 0]

      pos = [1, 2 , 1, 1]

      glLightfv(GL_LIGHT0, GL_DIFFUSE, color);
      glLightfv(GL_LIGHT0, GL_POSITION, pos);

      ambientColor = [0.2, 0.2, 0.2, 1.0 ]
      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor)

    end

    def update
        
        return if @note_index == -1
        
        @note_x += 0.4
        
        if milliseconds - @old_time > note_time
            puts current_note
            advance_note
            @old_time = milliseconds
        end
        
        @angle += 1
        
        @note_x = -9 if @note_x > 10

    end

    def draw
        gl_init
        lights

        glScalef(1,SCREEN_WIDTH/SCREEN_HEIGHT.to_f,1)
        glScalef 0.1,0.1,0.1

        glTranslatef(@note_x,0,0)

        glColor3f 0.9,0,0

        glRotate(@angle,0,1,0)
        draw_obj 'cube.obj'
    end

    def button_down(id)
      exit if id == KbEscape
      
      if id == KbReturn
          if @note_index == -1
            @note_index = 0
          else
            @note_index = -1
          end
      end
      
    end

end

Game.new.show
